package com.example.chatservice.webSocket;


import com.example.chatdbservice.mapper.ChatSessionUserMapper;
import com.example.chatservice.mapper.ChatMessageMapper;
import com.example.userservice.mapper.UserContactApplyMapper;
import com.example.userservice.mapper.UserInfoMapper;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.example.config.redis.RedisComponent;
import org.example.entity.constants.Constants;
import org.example.entity.dto.MessageSendDto;
import org.example.entity.dto.WsInitData;
import org.example.entity.enums.MessageTypeEnum;
import org.example.entity.enums.ResponseCodeEnum;
import org.example.entity.enums.UserContactApplyStatusEnum;
import org.example.entity.enums.UserContactTypeEnum;
import org.example.entity.po.ChatMessage;
import org.example.entity.po.ChatSessionUser;
import org.example.entity.po.UserContactApply;
import org.example.entity.po.UserInfo;
import org.example.entity.query.ChatMessageQuery;
import org.example.entity.query.ChatSessionUserQuery;
import org.example.entity.query.UserContactApplyQuery;
import org.example.entity.query.UserInfoQuery;
import org.example.exception.BusinessException;
import org.example.utils.JsonUtils;
import org.example.utils.StringTools;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description
 * @Author susu
 * @Date 2024/9/19
 */

@Component
@Slf4j
public class ChannelContextUtils {

    private static final ConcurrentHashMap<String,Channel> USER_CONTEXT_MAP = new ConcurrentHashMap<>();

    private static final ConcurrentHashMap<String, ChannelGroup> GROUP_CONTEXT_MAP = new ConcurrentHashMap<>();
    @Resource
    private RedisComponent redisComponent;
//    @Resource
//    private ChatSessionUserService chatSessionUserService;
    @Resource
    private ChatSessionUserMapper<ChatSessionUser, ChatSessionUserQuery> chatSessionUserMapper;
    @Resource
    private UserInfoMapper<UserInfo, UserInfoQuery> userInfoMapper;
    @Resource
    private ChatMessageMapper<ChatMessage, ChatMessageQuery> chatMessageMapper;
    @Resource
    private UserContactApplyMapper<UserContactApply, UserContactApplyQuery> userContactApplyMapper;
    public void addContext(String userId, Channel channel){
        //存储userid至channel中
        String channelId = channel.id().toString();
        AttributeKey attributeKey = null;
        if (!AttributeKey.exists(channelId)){
            attributeKey = AttributeKey.newInstance(channelId);
        }else {
            attributeKey = AttributeKey.valueOf(channelId);
        }
        channel.attr(attributeKey).set(userId);

        USER_CONTEXT_MAP.put(userId,channel);
        redisComponent.saveHeartBeat(userId);

        //将自己加入群组channel
        List<String> myContact = redisComponent.getMyContact(userId);
        List<String> groupIdList = new ArrayList<>();
        for (String groupId : myContact) {
            if (groupId.startsWith(UserContactTypeEnum.GROUP.getPrefix())){
                add2Group(groupId,channel);
                //获得群组id
                groupIdList.add(groupId);
            }
        }
        //更新最后登录时间
        UserInfo userInfo = new UserInfo();
        userInfo.setLastLoginTime(new Date());
        userInfoMapper.updateByUserId(userInfo,userId);

        //给用户发送消息
        UserInfo info = userInfoMapper.selectByUserId(userId);
        Long lastOffTime = info.getLastOffTime();
        if (lastOffTime != null && (System.currentTimeMillis() - lastOffTime > Constants.MILLISECOND_3DAYS_AGO)){
            lastOffTime = System.currentTimeMillis() - Constants.MILLISECOND_3DAYS_AGO;
        }

        /**
         * 1.查询会话信息 查询用户所有会话信息，保证换了设备会话同步
         */
        ChatSessionUserQuery query = new ChatSessionUserQuery();
        query.setUserId(userId);
        query.setOrderBy("last_receive_time desc");
        List<ChatSessionUser> listByParam = chatSessionUserMapper.selectList(query);

        WsInitData wsInitData = new WsInitData();
        wsInitData.setChatSessionList(listByParam);

        /**
         * 2.查询聊天信息
         */
        groupIdList.add(userId);
        ChatMessageQuery messageQuery = new ChatMessageQuery();
        messageQuery.setContactIdList(groupIdList);
        messageQuery.setLastReceiveTime(lastOffTime);
        List<ChatMessage> messageList = chatMessageMapper.selectList(messageQuery);
        wsInitData.setChatMessageList(messageList);


        /**
         * 3.查询好友申请
         */
        UserContactApplyQuery applyQuery = new UserContactApplyQuery();
        applyQuery.setReceiveUserId(userId);
        applyQuery.setStatus(UserContactApplyStatusEnum.INIT.getStatus());
        applyQuery.setLastApplyTimestamp(lastOffTime);

        Integer pageTotal = userContactApplyMapper.selectCount(applyQuery);
        wsInitData.setApplyCount(pageTotal);

        //发送消息
        MessageSendDto dto = new MessageSendDto();
        dto.setMessageType(MessageTypeEnum.INIT.getType());
        dto.setContactId(userId);
        dto.setExtendData(wsInitData);
        sendMsg(dto,userId);
    }

    public void sendMsg(MessageSendDto messageSendDto , String reciveId){
        if (reciveId == null){
            return;
        }
        Channel channel = USER_CONTEXT_MAP.get(reciveId);
        if (channel == null){
            return;
        }

        if (MessageTypeEnum.ADD_FRIEND_SELF.getType().equals(messageSendDto.getMessageType())){

            UserInfo extendData = (UserInfo) messageSendDto.getExtendData();
            messageSendDto.setMessageType(MessageTypeEnum.ADD_FRIEND.getType());
            messageSendDto.setContactId(extendData.getUserId());
            messageSendDto.setContactName(extendData.getNickName());
            messageSendDto.setExtendData(null);
        }else {
            //相对于客户端而言，联系人就是发送人

            messageSendDto.setContactId(messageSendDto.getSendUserId());
            messageSendDto.setContactName(messageSendDto.getSendUserNickName());
        }
        log.info("发送人{}",messageSendDto.getContactId());
        channel.writeAndFlush(new TextWebSocketFrame(JsonUtils.convertObj2Json(messageSendDto)));
    }

    public void sendMessage(MessageSendDto messageSendDto){
        UserContactTypeEnum contactTypeEnum = UserContactTypeEnum.getByPrefix(messageSendDto.getContactId());
        switch (contactTypeEnum){
            case USER:
                send2User(messageSendDto);
                break;
            case GROUP:
                send2Group(messageSendDto);
                break;
        }
    }

    //单聊发送给用户
    private void send2User(MessageSendDto messageSendDto){
        String contactId = messageSendDto.getContactId();
        if(!StringTools.isEmpty(contactId)) {
            sendMsg(messageSendDto, contactId);
        }
        //强制下线
        if (messageSendDto.getMessageType().equals(MessageTypeEnum.FORCE_OFF_LINE.getType())){
            closeChannel(contactId);
        }
    }

    //关闭channel
    private void closeChannel(String userId){
        Channel channel = USER_CONTEXT_MAP.get(userId);
        redisComponent.cleanUserTokenByUserId(userId);
        channel.close();
    }

    //发送给群组
    private void send2Group(MessageSendDto messageSendDto){
        String contactId = messageSendDto.getContactId();
        if(StringTools.isEmpty(contactId)) {
           return;
        }
        ChannelGroup channels = GROUP_CONTEXT_MAP.get(contactId);
        if (channels == null){
            return;
        }
        channels.writeAndFlush(new TextWebSocketFrame(JsonUtils.convertObj2Json(messageSendDto)));
    }
    private void add2Group(String groupId,Channel channel){
        ChannelGroup channels = GROUP_CONTEXT_MAP.get(groupId);
        if (channels == null){
            channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
            GROUP_CONTEXT_MAP.put(groupId,channels);
        }
        if (channel == null){
            return;
        }
        channels.add(channel);
    }
    public void addUser2Group(String userId, String groupId) {
        Channel channel = USER_CONTEXT_MAP.get(userId);
        add2Group(groupId, channel);
    }
//    public void addUser2Group(String userId,String groupId){
//        Channel channel = USER_CONTEXT_MAP.get(userId);
//        if (channel == null){
//            return;
//        }
//        ChannelGroup channels = GROUP_CONTEXT_MAP.get(groupId);
//        if (channels == null){
//            channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
//            GROUP_CONTEXT_MAP.put(groupId,channels);
//        }
//        channels.add(channel);
//    }
//    public void removeContext(Channel channel){
//        Attribute<String> attr = channel.attr(AttributeKey.valueOf(channel.id().toString()));
//        String userId = attr.get();
//        if (!StringTools.isEmpty(userId)){
//            USER_CONTEXT_MAP.remove(userId);
//        }
//        redisComponent.removeUserHeartBeat(userId);
//        //更新最后离线时间
//        UserInfo userInfo = new UserInfo();
//        userInfo.setLastOffTime(System.currentTimeMillis());
//        userInfoMapper.updateByUserId(userInfo,userId);
//    }

    public void removeContext(String userId){
        Channel channel = USER_CONTEXT_MAP.get(userId);
//        Attribute<String> attr = channel.attr(AttributeKey.valueOf(channel.id().toString()));
        if (!StringTools.isEmpty(userId)){
            USER_CONTEXT_MAP.remove(userId);
        }
        redisComponent.removeUserHeartBeat(userId);
        //更新最后离线时间
        UserInfo userInfo = new UserInfo();
        userInfo.setLastOffTime(System.currentTimeMillis());
        userInfoMapper.updateByUserId(userInfo,userId);
    }

    public void closeContext(String userId) {
        if (userId == null){
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }

        Channel channel = USER_CONTEXT_MAP.get(userId);
        if (channel == null){
            return;
        }
        channel.close();
    }
}
